﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Hosting;
using System.Web.Security;
using System.Web.UI;
using Univar.Helpers;

namespace Univar
{
	public static partial class Storage
	{
		/// <summary>
		/// A class that provides in file caching ability on the server.
		/// </summary>
		public static class InFileCache
		{
			static object _lockKey = new object();

			/// <summary>
			/// The upper size limit in bytes for each cache file.
			/// Default is 10MB.
			/// </summary>
			public static double MaximumFileSize = 10 * 1024 * 1024;

			public struct InFileCacheData
			{
				public DateTime? ExpiryDate;
				public string Data;
			}

			public static TimeSpan DefaultLifeTime = TimeSpan.FromDays(100);

			/// <summary>
			/// Returns the path for the session file. The value can be stored in the 
			/// web.config. The keys InFileCacheFolderPath and InFileCacheDeployedFolderPath are used
			/// to specify the folder path used when running from the IDE or within IIS respectively.
			/// The default value for both are ~\UserInFileCaches and c:\UserInFileCaches respectively. Note that
			/// the default path used when running the website within IIS is located outside the IIS folder itself.
			/// This is to avoid it being overwritten on subsequent builds and to avoid access rights issues.
			/// Also when deployed on a server the specified folder needs to be given read/write access rights.
			/// </summary>
			public static string DefaultFolderPath
			{
				get
				{
					//if (SecurityManager.IsGranted( new AspNetHostingPermission(AspNetHostingPermissionLevel.Medium)))

					// Return a path depending on whether the website is running from the IDE or from within IIS
					if ((StorageUser.Context == null && Debugger.IsAttached)
					   || (StorageUser.Context != null && string.IsNullOrEmpty(StorageUser.Context.Request.ServerVariables["SERVER_SOFTWARE"])))
						return ConfigurationManager.AppSettings.Get("InFileCacheDeployedFolderPath") ?? @"c:\InFileCache";
					else
						return ConfigurationManager.AppSettings.Get("InFileCacheFolderPath") ?? @"~\InFileCache";
				}
			}

			public static T Get<T>(string key)
			{
				return Serializer.Deserialize<T>(Get(null, key, false), JsonEncoding.None, true);
			}


			public static T Get<T>(string key, bool decrypt)
			{
				return Serializer.Deserialize<T>(Get(null, key, decrypt), JsonEncoding.None, true);
			}

			public static T Get<T>(string folderPath, string fileName, string key, bool decrypt)
			{
				return Serializer.Deserialize<T>(Get(folderPath, fileName, key, decrypt), JsonEncoding.None, true);
			}	
			
			public static string Get(string key)
			{
				return Get(null, key, false);
			}

			public static string Get(string key, bool decrypt)
			{
				string filePath = GetFilePath(key);
				return Get(null, filePath, key, decrypt);
			}

			public static string Get(string folderPath, string key, bool decrypt)
			{
				string filePath = GetFilePath(folderPath, key);
				return Get(folderPath, filePath, key, decrypt);
			}

			public static string Get(string folderPath, string fileName, string key, bool decrypt)
			{
				fileName = GetFilePath(folderPath ?? DefaultFolderPath, fileName);

				if (!File.Exists(fileName))
					return null;

				Dictionary<string, InFileCacheData> dictionary;
				try
				{
					dictionary = Serializer.Deserialize<Dictionary<string, InFileCacheData>>(File.ReadAllText(fileName), true);

					if (dictionary != null && dictionary.Keys.Contains(key))
					{
						if (DateTime.Now > (dictionary[key].ExpiryDate ?? DateTime.MaxValue))
							Set<object>(folderPath, fileName, key, null, null, false, true);
						else
							return decrypt
								? CookieEncryptor.Decrypt(dictionary[key].Data, true)
								: dictionary[key].Data;
					}
				}
				catch (Exception)
				{
					return null;
				}
				return null;
			}

			public static void Set<T>(string key, T value)
			{
				Set<T>(null, key, value, null, false, false);
			}

			public static void Set<T>(string key, T value, bool encrypt)
			{
				Set<T>(null, key, value, null, encrypt, false);
			}

			public static void Set<T>(string key, T value, TimeSpan? lifeTime, bool encrypt)
			{
				Set<T>(null, key, value, lifeTime, encrypt, false);
			}

			public static void Set<T>(string folderPath, string key, T value, TimeSpan? lifeTime, bool encrypt, bool throwExceptionOnSerializationError)
			{
				string filePath = GetFilePath(folderPath, key);//, useProfileNameIfAvailable);
				Set<T>(folderPath, filePath, value, lifeTime, encrypt, throwExceptionOnSerializationError);
			}

			public static void Set<T>(string folderPath, string fileName, string key, T value, TimeSpan? lifeTime, bool encrypt, bool throwExceptionOnSerializationError)
			{
				InFileCacheData InFileCacheData;

				string filePath = GetFilePath(folderPath, fileName);

				if (filePath == null)
					throw new FieldAccessException("session file could not be created under the path " + filePath);

				if (!File.Exists(filePath))
				{
					File.CreateText(filePath).Close();
					//AddAccessRights(filePath, "IIS_IUSRS", FileSystemRights.Modify);
				}
				else
				{
					if (GetSize(false, folderPath, key) > MaximumFileSize)
						Set<object>(folderPath, fileName, key, null, null, false, false);
				}

				try
				{
					Dictionary<string, InFileCacheData> dataDictionary =
						Serializer.Deserialize<Dictionary<string, InFileCacheData>>(File.ReadAllText(filePath), throwExceptionOnSerializationError);

					if (dataDictionary == null)
						dataDictionary = new Dictionary<string, InFileCacheData>();

					if (value == null)
					{
						dataDictionary.Remove(key);
					}
					else
					{
						InFileCacheData = new InFileCacheData();
						if (lifeTime.HasValue)
							InFileCacheData.ExpiryDate = DateTime.Now.Add(lifeTime.Value);

						InFileCacheData.Data = Serializer.Serialize<T>(value, JsonEncoding.None, throwExceptionOnSerializationError);

						if (encrypt)
							InFileCacheData.Data = CookieEncryptor.Encrypt(InFileCacheData.Data);

						if (dataDictionary.Keys.Contains(key))
							dataDictionary[key] = InFileCacheData;
						else
							dataDictionary.Add(key, InFileCacheData);
					}

					string strValue = Serializer.Serialize<Dictionary<string, InFileCacheData>>(dataDictionary, JsonEncoding.None, throwExceptionOnSerializationError);

					try
					{
						lock (_lockKey)
						{
							using (StreamWriter sw = new StreamWriter(filePath, false, System.Text.Encoding.UTF8))
							{
								sw.Write(strValue.Replace("\r\n", Environment.NewLine));
							}
						}
					}
					catch (FieldAccessException ex)
					{
						throw ex;
					}
				}
				catch (Exception ex)
				{
					//AddAccessRights(new FileInfo(filePath).Directory.FullName, "IIS_IUSRS", FileSystemRights.Modify);
					StorageUser.Context.Response.Write(ex.Message);
					throw ex;
				}
			}

			//private static void AddAccessRights(string folderPath, string user, FileSystemRights rights)
			//{
			//    try
			//    {
			//        DirectoryInfo folder = new DirectoryInfo(folderPath);
			//        DirectorySecurity dSecurity = folder.GetAccessControl();
			//        FileSystemAccessRule fsar = new FileSystemAccessRule(user, rights, AccessControlType.Allow);
			//        dSecurity.AddAccessRule(fsar);
			//        folder.SetAccessControl(dSecurity);
			//    }
			//    catch (Exception ex)
			//    {
			//        //User.Context.Response.Write(ex.Message);
			//        throw ex;
			//    }
			//}

			public static void Remove(string key)
			{
				Set<object>(key, null);
			}

			public static void Remove(string key, Scopes scope)
			{
				key = StorageUser.GetUserKeyByScope(key, scope, null, null);
				Set<object>(key, null);
			}

			//public static string GetUserId(bool useProfileNameIfAvailable)
			//{   //page.Request.LogonUserIdentity
			//    if (useProfileNameIfAvailable && !CacheUser.Context.Profile.IsAnonymous)
			//        return Membership.GetUser(CacheUser.Context.Profile.UserName).ProviderUserKey.ToString();

			//    // Get unique user id generated when no profile name was found.
			//    string userId = Stores.Cookie.Get(CacheUser.DefaultUserNameKey, false, true);
			//    if (userId != null)
			//        return userId;
			//    else // User has no associated record stored on the server.
			//        return CacheUser.CreateIdentifierCookie();
			//}

			public static string GetFilePath(string userFileName)
			{
				return GetFilePath(null, userFileName);
			}

			public static string GetFilePath(string folderPath, string fileName)
			{
				folderPath = GetFolder(DefaultFolderPath, true);
				return Path.Combine(folderPath, HttpUtility.UrlEncode(fileName) + ".txt");
			}

			public static string GetFolder(bool createWhenNotFound)
			{
				return GetFolder(null, createWhenNotFound);
			}

			public static string GetFolder(string folderPath, bool createWhenNotFound)
			{
				if (string.IsNullOrEmpty(folderPath))
					folderPath = DefaultFolderPath;

				if (folderPath.StartsWith("~"))
					folderPath = HostingEnvironment.MapPath(folderPath);

				if (createWhenNotFound && !Directory.Exists(folderPath))
				{
					Directory.CreateDirectory(folderPath);
					//AddAccessRights(folderPath, "IIS_IUSRS", FileSystemRights.Modify);
				}
				return folderPath;
			}

			public static long GetSize(bool shared, string key)
			{
				return GetSize(shared, null, key);
			}

			public static long GetSize(bool shared, string folderPath, string key)
			{
				string userFileName = shared ? key : null;
				string filePath = GetFilePath(folderPath, userFileName);
				FileInfo file = new FileInfo(filePath);
				if (file.Exists)
					return file.Length;
				else
					return -1;
			}
		}
	}
}